Support inline-selection in entries (#318459)
authorXan Lopez <xan@src.gnome.org>
Fri, 27 Apr 2007 16:50:04 +0000 (16:50 +0000)
committerXan Lopez <xan@src.gnome.org>
Fri, 27 Apr 2007 16:50:04 +0000 (16:50 +0000)
Support inline-selection in entries (#318459)

* gtk/gtkentry.c:
* gtk/gtkentrycompletion.c:
* gtk/gtkentrycompletion.h:
* gtk/gtkentryprivate.h:

When enabled cursor-match is emited when the cursor is on
a possible completion on the list. The default implementation
will replace the contents on the entry with the contents of
the text column in the completion model.

Review and improvements by Matthias Clasen.

svn path=/trunk/; revision=17660

ChangeLog
gtk/gtkentry.c
gtk/gtkentrycompletion.c
gtk/gtkentrycompletion.h
gtk/gtkentryprivate.h

index c52305a94799371370ff940af6f07142b37df8a8..a4cb1f1f48cc50c6e5d7ad96026e0a8367e7ba5a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2007-04-27  Xan Lopez  <xan@gnome.org>
+
+       Support inline-selection in entries (#318459)
+       
+       * gtk/gtkentry.c:
+       * gtk/gtkentrycompletion.c:
+       * gtk/gtkentrycompletion.h:
+       * gtk/gtkentryprivate.h:
+
+       When enabled cursor-match is emited when the cursor is on
+       a possible completion on the list. The default implementation
+       will replace the contents on the entry with the contents of
+       the text column in the completion model.
+
+       Review and improvements by Matthias Clasen.
+
 2007-04-27  Michael Natterer  <mitch@imendio.com>
 
        Merged heavily modified patch from maemo-gtk which enables opening
index e6653d1a0dd8ae4dd7d85d921b84fe68d22468a3..992ac6f00bcb54390a58c1f9e3e88662873d8f50 100644 (file)
@@ -5679,6 +5679,22 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
           path = gtk_tree_path_new_from_indices (completion->priv->current_selected, -1);
           gtk_tree_view_set_cursor (GTK_TREE_VIEW (completion->priv->tree_view),
                                     path, NULL, FALSE);
+
+          if (completion->priv->inline_selection)
+            {
+
+              GtkTreeIter iter;
+              GtkTreeModel *model = NULL;
+              GtkTreeSelection *sel;
+              gboolean entry_set;
+
+              sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (completion->priv->tree_view));
+              if (!gtk_tree_selection_get_selected (sel, &model, &iter))
+                return FALSE;
+
+              g_signal_emit_by_name (completion, "cursor_on_match", model,
+                                     &iter, &entry_set);
+            }
         }
       else if (completion->priv->current_selected - matches >= 0)
         {
@@ -5693,11 +5709,23 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
 
       return TRUE;
     }
-  else if (event->keyval == GDK_Escape) 
+  else if (event->keyval == GDK_Escape ||
+           event->keyval == GDK_Left ||
+           event->keyval == GDK_KP_Left ||
+           event->keyval == GDK_Right ||
+           event->keyval == GDK_KP_Right) 
     {
       _gtk_entry_reset_im_context (GTK_ENTRY (widget));
       _gtk_entry_completion_popdown (completion);
 
+      if (completion->priv->inline_selection)
+        {
+          if (event->keyval == GDK_Escape)
+            gtk_editable_delete_selection (GTK_EDITABLE (widget));
+          /* Move the cursor to the end */
+          gtk_editable_set_position (GTK_EDITABLE (widget), -1);
+        }
+
       return TRUE;
     }
   else if (event->keyval == GDK_Tab || 
@@ -5750,7 +5778,7 @@ gtk_entry_completion_key_press (GtkWidget   *widget,
               /* move the cursor to the end */
               gtk_editable_set_position (GTK_EDITABLE (widget), -1);
 
-             g_free (str);
+              g_free (str);
             }
 
           return TRUE;
@@ -5880,12 +5908,18 @@ disconnect_completion_signals (GtkEntry           *entry,
                                       G_CALLBACK (completion_changed), entry);
   if (completion->priv->changed_id > 0 &&
       g_signal_handler_is_connected (entry, completion->priv->changed_id))
-    g_signal_handler_disconnect (entry, completion->priv->changed_id);
+    {
+      g_signal_handler_disconnect (entry, completion->priv->changed_id);
+      completion->priv->changed_id = 0;
+    }
   g_signal_handlers_disconnect_by_func (entry, 
                                        G_CALLBACK (gtk_entry_completion_key_press), completion);
   if (completion->priv->insert_text_id > 0 &&
       g_signal_handler_is_connected (entry, completion->priv->insert_text_id))
-    g_signal_handler_disconnect (entry, completion->priv->insert_text_id);
+    {
+      g_signal_handler_disconnect (entry, completion->priv->insert_text_id);
+      completion->priv->insert_text_id = 0;
+    }
   g_signal_handlers_disconnect_by_func (entry, 
                                        G_CALLBACK (completion_insert_text_callback), completion);
   g_signal_handlers_disconnect_by_func (entry, 
@@ -5919,6 +5953,7 @@ connect_completion_signals (GtkEntry           *entry,
       g_signal_connect (entry, "focus_out_event",
                        G_CALLBACK (accept_completion_callback), completion);
     }
+
   g_signal_connect (completion, "notify",
                    G_CALLBACK (completion_changed), entry);
 }
index d8bc08a06ae48969aa767aaffe8917b0820679d0..f4d74d62f44521f37612ac89a36009a577834550 100644 (file)
@@ -47,6 +47,7 @@ enum
   INSERT_PREFIX,
   MATCH_SELECTED,
   ACTION_ACTIVATED,
+  CURSOR_ON_MATCH,
   LAST_SIGNAL
 };
 
@@ -60,7 +61,8 @@ enum
   PROP_INLINE_COMPLETION,
   PROP_POPUP_COMPLETION,
   PROP_POPUP_SET_WIDTH,
-  PROP_POPUP_SINGLE_MATCH
+  PROP_POPUP_SINGLE_MATCH,
+  PROP_INLINE_SELECTION
 };
 
 #define GTK_ENTRY_COMPLETION_GET_PRIVATE(obj)(G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_ENTRY_COMPLETION, GtkEntryCompletionPrivate))
@@ -136,6 +138,9 @@ static gboolean gtk_entry_completion_match_selected      (GtkEntryCompletion *co
                                                          GtkTreeIter        *iter);
 static gboolean gtk_entry_completion_real_insert_prefix  (GtkEntryCompletion *completion,
                                                          const gchar        *prefix);
+static gboolean gtk_entry_completion_cursor_on_match     (GtkEntryCompletion *completion,
+                                                         GtkTreeModel       *model,
+                                                         GtkTreeIter        *iter);
 
 static guint entry_completion_signals[LAST_SIGNAL] = { 0 };
 
@@ -156,6 +161,7 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
 
   klass->match_selected = gtk_entry_completion_match_selected;
   klass->insert_prefix = gtk_entry_completion_real_insert_prefix;
+  klass->cursor_on_match = gtk_entry_completion_cursor_on_match;
 
   /**
    * GtkEntryCompletion::insert-prefix:
@@ -210,6 +216,32 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
                   G_TYPE_BOOLEAN, 2,
                   GTK_TYPE_TREE_MODEL,
                   GTK_TYPE_TREE_ITER);
+  /**
+   * GtkEntryCompletion::cursor-on-match:
+   * @widget: the object which received the signal
+   * @model: the #GtkTreeModel containing the matches
+   * @iter: a #GtkTreeIter positioned at the selected match
+   * 
+   * Gets emitted when a match from the cursor is on a match
+   * of the list.The default behaviour is to replace the contents
+   * of the entry with the contents of the text column in the row 
+   * pointed to by @iter.
+   *
+   * Return value: %TRUE if the signal has been handled
+   * 
+   * Since: 2.12
+   */ 
+
+  entry_completion_signals[CURSOR_ON_MATCH] =
+    g_signal_new (I_("cursor_on_match"),
+                 G_TYPE_FROM_CLASS (klass),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GtkEntryCompletionClass, cursor_on_match),
+                 _gtk_boolean_handled_accumulator, NULL,
+                 _gtk_marshal_BOOLEAN__OBJECT_BOXED,
+                 G_TYPE_BOOLEAN, 2,
+                 GTK_TYPE_TREE_MODEL,
+                 GTK_TYPE_TREE_ITER);
                  
   /**
    * GtkEntryCompletion::action-activated:
@@ -330,6 +362,21 @@ gtk_entry_completion_class_init (GtkEntryCompletionClass *klass)
                                                         P_("If TRUE, the popup window will appear for a single match."),
                                                         TRUE,
                                                         GTK_PARAM_READWRITE));
+  /**
+   * GtkEntryCompletion:inline-selection:
+   * 
+   * Determines whether the possible completions on the popup
+   * will appear in the entry as you navigate through them.
+   
+   * Since: 2.12
+   */
+  g_object_class_install_property (object_class,
+                                  PROP_INLINE_SELECTION,
+                                  g_param_spec_boolean ("inline-selection",
+                                                        P_("Inline selection"),
+                                                        P_("Your description here"),
+                                                        FALSE,
+                                                        GTK_PARAM_READWRITE));
 
   g_type_class_add_private (object_class, sizeof (GtkEntryCompletionPrivate));
 }
@@ -364,6 +411,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
   priv->popup_completion = TRUE;
   priv->popup_set_width = TRUE;
   priv->popup_single_match = TRUE;
+  priv->inline_selection = FALSE;
 
   /* completions */
   priv->filter_model = NULL;
@@ -378,6 +426,7 @@ gtk_entry_completion_init (GtkEntryCompletion *completion)
   g_signal_connect (priv->tree_view, "motion_notify_event",
                    G_CALLBACK (gtk_entry_completion_list_motion_notify),
                    completion);
+
   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
 
@@ -505,6 +554,10 @@ gtk_entry_completion_set_property (GObject      *object,
        priv->popup_single_match = g_value_get_boolean (value);
         break;
 
+      case PROP_INLINE_SELECTION:
+        priv->inline_selection = g_value_get_boolean (value);
+        break;
+      
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -550,6 +603,10 @@ gtk_entry_completion_get_property (GObject    *object,
         g_value_set_boolean (value, gtk_entry_completion_get_popup_single_match (completion));
         break;
 
+      case PROP_INLINE_SELECTION:
+        g_value_set_boolean (value, gtk_entry_completion_get_inline_selection (completion));
+        break;
+
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -1277,7 +1334,7 @@ gtk_entry_completion_list_motion_notify (GtkWidget      *widget,
 {
   GtkEntryCompletion *completion = GTK_ENTRY_COMPLETION (data);
 
-  completion->priv->ignore_enter = FALSE; 
+  completion->priv->ignore_enter = FALSE;
   
   return FALSE;
 }
@@ -1468,6 +1525,15 @@ gtk_entry_completion_match_selected (GtkEntryCompletion *completion,
   return TRUE;
 }
 
+static gboolean
+gtk_entry_completion_cursor_on_match (GtkEntryCompletion *completion,
+                                     GtkTreeModel       *model,
+                                     GtkTreeIter        *iter)
+{
+  gtk_entry_completion_insert_completion (completion, model, iter);
+
+  return TRUE;
+}
 
 static gchar *
 gtk_entry_completion_compute_prefix (GtkEntryCompletion *completion)
@@ -1565,6 +1631,65 @@ gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion,
   return TRUE;
 }
 
+void
+gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
+                                            const gchar *text)
+{
+  GtkEntryCompletionPrivate *priv = completion->priv;
+  gint len;
+
+  if (priv->changed_id > 0)
+    {
+      g_signal_handler_block (priv->entry,
+                             priv->changed_id);
+    }
+
+  if (priv->insert_text_id > 0)
+    {
+      g_signal_handler_block (completion->priv->entry,
+                             completion->priv->insert_text_id);
+    }
+
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (priv->entry), &len, NULL);
+  gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
+  gtk_editable_select_region (GTK_EDITABLE (priv->entry), len, -1);
+
+  if (priv->changed_id > 0)
+    {
+      g_signal_handler_unblock (priv->entry,
+                               priv->changed_id);
+    }
+
+  if (priv->insert_text_id > 0)
+    {
+      g_signal_handler_unblock (priv->entry,
+                               priv->insert_text_id);
+    }
+}
+
+gboolean
+gtk_entry_completion_insert_completion (GtkEntryCompletion *completion,
+                                       GtkTreeModel       *model,
+                                       GtkTreeIter        *iter)
+{
+  gchar *str = NULL;
+
+  g_return_val_if_fail (GTK_IS_TREE_MODEL (model), FALSE);
+
+  if (completion->priv->text_column < 0)
+    return FALSE;
+
+  gtk_tree_model_get (model, iter,
+                     completion->priv->text_column, &str,
+                     -1);
+
+  gtk_entry_completion_insert_completion_text (completion, str);
+
+  g_free (str);
+
+  return TRUE;
+}
+
 /**
  * gtk_entry_completion_insert_prefix:
  * @completion: a #GtkEntryCompletion
@@ -1573,6 +1698,7 @@ gtk_entry_completion_real_insert_prefix (GtkEntryCompletion *completion,
  * 
  * Since: 2.6
  **/
+
 void
 gtk_entry_completion_insert_prefix (GtkEntryCompletion *completion)
 {
@@ -1622,7 +1748,6 @@ gtk_entry_completion_set_inline_completion (GtkEntryCompletion *completion,
     }
 }
 
-
 /**
  * gtk_entry_completion_get_inline_completion:
  * @completion: a #GtkEntryCompletion
@@ -1782,6 +1907,29 @@ gtk_entry_completion_get_popup_single_match (GtkEntryCompletion *completion)
   return completion->priv->popup_single_match;
 }
 
+void
+gtk_entry_completion_set_inline_selection (GtkEntryCompletion *completion,
+                                          gboolean inline_selection)
+{
+  g_return_if_fail (GTK_IS_ENTRY_COMPLETION (completion));
+
+  inline_selection = inline_selection != FALSE;
+
+  if (completion->priv->inline_selection != inline_selection)
+    {
+      completion->priv->inline_selection = inline_selection;
+
+      g_object_notify (G_OBJECT (completion), "inline-selection");
+    }
+}
+
+gboolean
+gtk_entry_completion_get_inline_selection (GtkEntryCompletion *completion)
+{
+  g_return_val_if_fail (GTK_IS_ENTRY_COMPLETION (completion), FALSE);
+
+  return completion->priv->inline_selection;
+}
 
 #define __GTK_ENTRY_COMPLETION_C__
 #include "gtkaliasdef.c"
index 01a40fd957b344804fc73e1cd474ac302b0f5d93..26285d2ac64a51acfe801cd8d2c1a2352804f3c5 100644 (file)
@@ -65,11 +65,13 @@ struct _GtkEntryCompletionClass
                                  gint                index_);
   gboolean (* insert_prefix)    (GtkEntryCompletion *completion,
                                 const gchar        *prefix); 
+  gboolean (* cursor_on_match)  (GtkEntryCompletion *completion,
+                                GtkTreeModel       *model,
+                                GtkTreeIter        *iter);
 
   /* Padding for future expansion */
   void (*_gtk_reserved0) (void);
   void (*_gtk_reserved1) (void);
-  void (*_gtk_reserved2) (void);
 };
 
 /* core */
@@ -104,6 +106,9 @@ void                gtk_entry_completion_delete_action          (GtkEntryComplet
 void                gtk_entry_completion_set_inline_completion  (GtkEntryCompletion          *completion,
                                                                  gboolean                     inline_completion);
 gboolean            gtk_entry_completion_get_inline_completion  (GtkEntryCompletion          *completion);
+void                gtk_entry_completion_set_inline_selection  (GtkEntryCompletion          *completion,
+                                                                 gboolean                     inline_selection);
+gboolean            gtk_entry_completion_get_inline_selection  (GtkEntryCompletion          *completion);
 void                gtk_entry_completion_set_popup_completion   (GtkEntryCompletion          *completion,
                                                                  gboolean                     popup_completion);
 gboolean            gtk_entry_completion_get_popup_completion   (GtkEntryCompletion          *completion);
@@ -114,7 +119,8 @@ void                gtk_entry_completion_set_popup_single_match (GtkEntryComplet
                                                                  gboolean                     popup_single_match);
 gboolean            gtk_entry_completion_get_popup_single_match (GtkEntryCompletion          *completion);
 
-
+void                gtk_entry_completion_insert_completion_text (GtkEntryCompletion *completion,
+                                                                const gchar *text);
 /* convenience */
 void                gtk_entry_completion_set_text_column        (GtkEntryCompletion          *completion,
                                                                  gint                         column);
index 6589a0c2017aa93795df0b4592366e818da61c2a..696f744401ce43e7b7502dcfb1d519a6eb022847 100644 (file)
@@ -64,6 +64,8 @@ struct _GtkEntryCompletionPrivate
   guint popup_completion  : 1;
   guint popup_set_width   : 1;
   guint popup_single_match : 1;
+  guint inline_selection   : 1;
+
   GSource *check_completion_idle;
 };